package handle

import (
	"gitchina/CPTT4WeChat/data"
	"gitchina/CPTT4WeChat/protobuf"
	"gitchina/CPTT4WeChat/util"
	"github.com/golang/protobuf/proto"
)

type User struct{}

func (u User) AddOrUpdate(p *protobuf.UserBatch) {
	var repo data.ApiRepo
	for _, b := range p.GetBatch() {
		if info, ok := u.GetByUid(b.GetUid()); !ok {
			var g Garden
			if garden, ok := g.GetByUid(b.GetGardenUid()); ok {
				if _, err := repo.AddUser(data.UserDto{
					GardenId:  int(garden.GetId()),
					Uid:       b.GetUid(),
					UserNo:    b.GetUserNo(),
					Name:      b.GetName(),
					UserType:  int(b.GetUserType()),
					Signature: b.GetSignature(),
				}); err != nil {
					util.GetLogger().Error("[handle-AddOrUpdateUser] - error: %s", err.Error())
				}
			} else {
				util.GetLogger().Error("[handle-AddOrUpdateUser] - error: GardenUid:%s not found.", b.GetGardenUid())
			}
		} else {
			if b.GetSignature() != info.GetSignature() {
				if updated, err := repo.UpdateUser(data.UserDto{
					Id:        info.GetId(),
					UserNo:    b.GetUserNo(),
					Name:      b.GetName(),
					UserType:  int(b.GetUserType()),
					Signature: b.GetSignature(),
				}); err != nil {
					util.GetLogger().Error("[handle-AddOrUpdateUser] - error: %s", err.Error())
				} else {
					if updated {
						if cache, ok := util.GetCache(util.User_Cache); ok {
							cache.Delete(b.GetUid())
							if info.GetOpenId() != "" {
								cache.Delete(info.GetOpenId())
							}
						}
					}
				}
			}
		}
	}
}

func (u User) Bind(fromUserName, uid, nickName string) (bindOk bool) {
	if info, ok := u.GetByUid(uid); ok {
		if info.GetOpenId() != fromUserName {
			var repo data.ApiRepo
			if bindUserOk, err := repo.BindUser(data.UserDto{
				Id:       info.GetId(),
				OpenId:   fromUserName,
				NickName: nickName,
			}); err != nil {
				util.GetLogger().Error("[handle-BindUser] - error: %s", err.Error())
			} else {
				if bindUserOk {
					if cache, ok := util.GetCache(util.User_Cache); ok {
						cache.Delete(info.GetUid())
						cache.Delete(fromUserName)
						bindOk = true
					}
				}
			}
		} else {
			//ignore binding operation but returning ok
			bindOk = true
		}
	}
	return
}

func (u User) GetByUid(uid string) (p *protobuf.User, ok bool) {
	return u.getByIdentifier(uid, func(uid string) (data.UserDto, error) {
		var repo data.ApiRepo
		return repo.GetUserByUid(uid)
	})
}

func (u User) GetByOpenId(openId string) (p *protobuf.User, ok bool) {
	return u.getByIdentifier(openId, func(openId string) (data.UserDto, error) {
		var repo data.ApiRepo
		return repo.GetUserByOpenId(openId)
	})
}

func (u User) getByIdentifier(identifier string, getFn func(identifier string) (data.UserDto, error)) (p *protobuf.User, ok bool) {
	var cache util.Cache
	var val interface{}
	if cache, ok = util.GetCache(util.User_Cache); ok {
		if val, ok = cache.Get(identifier); ok {
			p = val.(*protobuf.User)
		} else {
			if dto, err := getFn(identifier); err == nil {
				p = &protobuf.User{
					Id:        proto.Int64(dto.Id),
					GardenUid: proto.String(dto.GardenUid),
					GardenId:  proto.Int32(int32(dto.GardenId)),
					Uid:       proto.String(dto.Uid),
					OpenId:    proto.String(dto.OpenId),
					UserNo:    proto.String(dto.UserNo),
					Name:      proto.String(dto.Name),
					NickName:  proto.String(dto.NickName),
					UserType:  proto.Int32(int32(dto.UserType)),
					Signature: proto.String(dto.Signature),
				}
				cache.Set(p.GetUid(), p)
				if p.GetOpenId() != "" {
					cache.Set(p.GetOpenId(), p)
				}
				ok = true
			} else {
				util.GetLogger().Error("[handle-GetByIdentifier] - error: %s", err.Error())
			}
		}
	}
	return
}
